
const jsb = (<any>window).jsb;
import { _decorator, Asset, assetManager, Component, EventHandler, game, sys } from "cc";
import { ConfigDataMgr } from "../../managers/ConfigDataMgr";
import { GlobelMgr } from "../../managers/GlobelMgr";
import { LogMgr } from "../../components/LogMgr";
/**检测是否需要更新返回*/
export enum CheckCallbackType {
    /**找不到本地热更新配置文件*/
    ERROR_NO_LOCAL_MANIFEST = "error_no_local_manifest",
     /**远程热更新配置文件加载失败*/
    ERROR_PARSE_MANIFEST = "error_parse_manifest",
     /**已是最新版本*/
    ALREADY_UP_TO_DATE ="already_up_to_date",
     /**有新版本*/
    NEW_VERSION_FOUND = "new_version_found",
     /**有新版本并更新到一半 返回额外2个参数percent，msg*/
    UPDATE_PROGRESSION = "update_progression",
}
/**监听更新是否成功*/
export enum UpdateCallbackType {
    /**没有发现本地manifest文件*/
    ERROR_NO_LOCAL_MANIFEST = "error_no_local_manifest", 
    /**找不到远程manifest文件 */
    ERROR_PARSE_MANIFEST = "error_parse_manifest",
        /** 已是最新版本*/
    ALREADY_UP_TO_DATE = "already_up_to_date",
        /**更新完毕 */
    UPDATE_FINISHED = "update_finished",
    /**更新失败*/
    FAIL = "fail",
}
const { ccclass, property } = _decorator;

@ccclass
export default class HotUpdateScript extends Component {
    /**本地存放热更新配置文件文件夹名*/
    @property
    folderName: string = "remote-hot";
    /**第一版本的mainfest*/
    @property(Asset)
    manifestUrl:Asset = null;
    /**监听检测是否需要更新状态返回 CheckCallbackType*/
    @property(EventHandler) onCheckCallbackEvent: EventHandler[] = []
      /**监听更新是否成功返回 UpdateCallbackType*/
     @property(EventHandler) onUpdateCallbackEvent: EventHandler[] = []
    /**监听更新进度会有percent,msg两个参数*/
    @property(EventHandler) onUpdateProgressionEvent: EventHandler[] = []
    /**监听获取本地manifest版本号*/
    @property(EventHandler) onLocalVersionEvent: EventHandler[] = []
    _assetManager: jsb.AssetsManager = null;
    _updating: boolean = false;
    _canRetry: boolean = false;
    _failCount: number = 0;//加载失败次数
    _maxFailCount:number = 50;//当加载失败重新加载多少次后将不再重新加载
    customManifestStr: string;//本地当前用的manifest数据
    /**本地存放热更新配置文件的文件夹路径*/
    _storagePath: string;
    /**本地manifest文件路径*/
    _storageManifestPath:string;
    onLoad() {
    }
    public startScript():void{
        this.initHostCdn();
        this.checkUpdate();
    }
    /**检测是否需要更新*/
    private checkUpdate() {
        if (this._updating) {
             LogMgr.log("检测更新中...");
            return;
        }
        if (this._assetManager.getState() === jsb.AssetsManager.State.UNINITED) {
             LogMgr.log("加载 热更配置");
            var url = this.manifestUrl.nativeUrl;
             LogMgr.log("路径="+url);
            this._assetManager.loadLocalManifest(url);
        }
        if (!this._assetManager.getLocalManifest() || !this._assetManager.getLocalManifest().isLoaded()) {
             LogMgr.log("加载manifest文件失败");
            return;
        }
         LogMgr.log("正式检查更新流程");

        this._assetManager.setEventCallback(this.onCheckCallback.bind(this));
        this._assetManager.checkUpdate();
        this._updating = true;
    }
    public hotUpdate() {
        if (this._assetManager && !this._updating) {
            this._assetManager.setEventCallback(this.onUpdateCallback.bind(this));
            if (this._assetManager.getState() === jsb.AssetsManager.State.UNINITED) {
                var url = this.manifestUrl.nativeUrl;
                this._assetManager.loadLocalManifest(url);
            }
            this._failCount = 0;
            this._assetManager.update();
            this._updating = true;
        }
    }
    private initHostCdn() {
        this._storagePath = ((jsb.fileUtils ? jsb.fileUtils.getWritablePath() : '/') + this.folderName);
        this._storageManifestPath = this._storagePath+"/project.manifest";
        let hostcdn = ConfigDataMgr.instance.getHotCdnPath();
        let customObj:any;
        if (!(jsb.fileUtils.isFileExist(this._storageManifestPath))) {
            this.customManifestStr = (this.manifestUrl as any)._nativeAsset;
            customObj = JSON.parse(this.customManifestStr);
            customObj.packageUrl = hostcdn;
            customObj.remoteManifestUrl = hostcdn +"project.manifest";
            customObj.remoteVersionUrl = hostcdn + "version.manifest";

            this.customManifestStr = JSON.stringify(customObj);
            if (!jsb.fileUtils.isDirectoryExist(this._storagePath)) {
                jsb.fileUtils.createDirectory(this._storagePath);
            }
           jsb.fileUtils.writeStringToFile(this.customManifestStr,this._storageManifestPath);
        } else {
                this.customManifestStr = jsb.fileUtils.getStringFromFile(this._storageManifestPath);
                customObj = JSON.parse(this.customManifestStr);
                if(customObj.packageUrl!=hostcdn)
                {
                    customObj.packageUrl = hostcdn;
                    customObj.remoteManifestUrl = hostcdn + "project.manifest";
                    customObj.remoteVersionUrl = hostcdn + "version.manifest";
                    this.customManifestStr = JSON.stringify(customObj);
                    jsb.fileUtils.writeStringToFile(this.customManifestStr,this._storageManifestPath);
                }
        }
        EventHandler.emitEvents(this.onLocalVersionEvent,customObj.version);
        this.initUpdate()
        //    }
    }
    private initUpdate() {
        this._assetManager = new jsb.AssetsManager("", this._storagePath, this.versionCompareHandle);//这里先不传第一个参数就不会触发自动更新
        this._assetManager.setVerifyCallback(function (filePath, asset) {
            //没有加密直接返回
            return true;
        });

        if (sys.os === "Android") {
            //一些安卓设备不支持同时下载文件过多
            assetManager.presets['default'].maxConcurrency = 2;
        }

    }
    private versionCompareHandle(versionA, versionB) //版本比较函数
    {
         LogMgr.log("版本号对比versionA="+versionA+">>>versionB"+versionB);
        var vA = versionA.split('.');
        var vB = versionB.split('.');
        for (var i = 0; i < vA.length; ++i) {
            var a = parseInt(vA[i]);
            var b = parseInt(vB[i] || 0);
            if (a === b) {
                continue;
            } else {
                return a - b;
            }
        }
        if (vB.length > vA.length) {
            return -1;
        }
        else {
            return 0;
        }


    }

    onDestroy() {
        if (sys.isNative && this._assetManager != null) {
            this._assetManager.setEventCallback(null);
            this._assetManager = null;

        }

    }


   private retry() {
        if (!this._updating && this._canRetry) {
            this._canRetry = false;
            this._assetManager.downloadFailedAssets();
        }
    }
    private onUpdateCallback(event) {
        let needRestart = false;
        let failed = false;
        switch (event.getEventCode()) {
            case jsb.EventAssetsManager.ERROR_NO_LOCAL_MANIFEST:
                 LogMgr.log("没有发现本地manifest文件");
                EventHandler.emitEvents(this.onUpdateCallbackEvent,UpdateCallbackType.ERROR_PARSE_MANIFEST);
                failed = true;
                break;
            //更新进度
            case jsb.EventAssetsManager.UPDATE_PROGRESSION:
                let percent = event.getPercent();
                if (isNaN(percent)) return;
                var msg = event.getMessage();
                EventHandler.emitEvents(this.onUpdateProgressionEvent,percent,msg);
                 LogMgr.log("updateCallback更新进度：" + percent + ', msg: ' + msg);
                break;

            //下载manifest文件失败，跳过热更新
            case jsb.EventAssetsManager.ERROR_DOWNLOAD_MANIFEST:
            case jsb.EventAssetsManager.ERROR_PARSE_MANIFEST:
                 LogMgr.log("下载manifest文件失败");
                EventHandler.emitEvents(this.onUpdateCallbackEvent,UpdateCallbackType.ERROR_PARSE_MANIFEST);
                failed = true;
                break;

            //已是最新版本
            case jsb.EventAssetsManager.ALREADY_UP_TO_DATE:
                 LogMgr.log("已是最新版本.");
                EventHandler.emitEvents(this.onUpdateCallbackEvent,UpdateCallbackType.ALREADY_UP_TO_DATE);
                break;
            //更新结束
            case jsb.EventAssetsManager.UPDATE_FINISHED:
                 LogMgr.log("更新结束." + event.getMessage());
                needRestart = true;
                break;
            //更新错误
            case jsb.EventAssetsManager.UPDATE_FAILED:
                 LogMgr.log("更新错误." + event.getMessage());
                this._updating = false;
                this._canRetry = true;
                this._failCount++;
                if(this._failCount<this._maxFailCount)
                {
                    this.retry();
                }else{
                    EventHandler.emitEvents(this.onUpdateCallbackEvent,UpdateCallbackType.FAIL);
                }
                break;
            //更新过程中错误
            case jsb.EventAssetsManager.ERROR_UPDATING:
                EventHandler.emitEvents(this.onUpdateCallbackEvent,UpdateCallbackType.FAIL);
                failed = true;
                 LogMgr.log('更新过程中错误: ' + event.getAssetId() + ', ' + event.getMessage());
                break;
            //解压错误
            case jsb.EventAssetsManager.ERROR_DECOMPRESS:
                failed = true;
                 LogMgr.log('解压错误');
                EventHandler.emitEvents(this.onUpdateCallbackEvent,UpdateCallbackType.FAIL);
                break;
            default:
                break;
        }

        if (failed) {
            this._assetManager.setEventCallback(null);
            this._updating = false;
        }

        if (needRestart ) {
            this._assetManager.setEventCallback(null);
            var searchPaths = jsb.fileUtils.getSearchPaths();
            var newPaths = this._assetManager.getLocalManifest().getSearchPaths();
            Array.prototype.unshift.apply(searchPaths, newPaths);
            jsb.fileUtils.setSearchPaths(searchPaths);
            sys.localStorage.setItem('HotUpdateSearchPaths', JSON.stringify(searchPaths));
            EventHandler.emitEvents(this.onUpdateCallbackEvent,UpdateCallbackType.UPDATE_FINISHED);
        } 
    }
    //检测更新状态
    private onCheckCallback(event) {
        switch (event.getEventCode()) {
            case jsb.EventAssetsManager.ERROR_NO_LOCAL_MANIFEST:
                 LogMgr.log("没有发现本地manifest文件");
                EventHandler.emitEvents(this.onCheckCallbackEvent,CheckCallbackType.ERROR_NO_LOCAL_MANIFEST);
                break;

            case jsb.EventAssetsManager.ERROR_DOWNLOAD_MANIFEST:

            case jsb.EventAssetsManager.ERROR_PARSE_MANIFEST:
                this.showLog("下载manifest文件失败");
                EventHandler.emitEvents(this.onCheckCallbackEvent,CheckCallbackType.ERROR_PARSE_MANIFEST);
                break;

            case jsb.EventAssetsManager.ALREADY_UP_TO_DATE:
                this.showLog("已更新.");
                EventHandler.emitEvents(this.onCheckCallbackEvent,CheckCallbackType.ALREADY_UP_TO_DATE);
                break;
            case jsb.EventAssetsManager.NEW_VERSION_FOUND: {
                //有新版本
                this.showLog("有新版本,需要更新");
                EventHandler.emitEvents(this.onCheckCallbackEvent,CheckCallbackType.NEW_VERSION_FOUND);
                this._updating = false;
                return;
            }
            case jsb.EventAssetsManager.UPDATE_PROGRESSION: {
                //有新版本
                let percent = event.getPercent();
                if (isNaN(percent)) return;
                var msg = event.getMessage();
                EventHandler.emitEvents(this.onCheckCallbackEvent,CheckCallbackType.UPDATE_PROGRESSION,percent,msg);
                this.showLog("checkCallback更新进度：" + percent + ', msg: ' + msg);
                return;
            }
            default:
                console.log('event.getEventCode():' + event.getEventCode());
                return;
        }
        this._assetManager.setEventCallback(null);
        this._updating = false;
    }
    showLog(text: string) {
         LogMgr.log(text);
    }
   
    // update (dt) {}


}
